﻿#include<iostream>
using namespace std;
//面向对象特性之一： 继承（封装 继承 多态）

//成员重复（公共）的类，多项重复的（冗杂），使用继承的方法可以减少步骤
//定义一个公共的复类：每个人都需要的成员信息，放在一个base类，其他的类去继承它(可以做增加成员信息)
//继承的本质是复用

//继承的语法：
//假定Person是基类
//      派生类   继承方式  父类
//class Student :public Person

//protected（受保护的）

//class Person
//{
//public:
//	void Print()
//	{
//		cout << "name:" << _name << endl;
//		cout << "age:" << _age << endl;
//	}
////protected:
//	string _name = "peter";//姓名
//	int _age = 18;//年龄
//	//...
//};
//
//class Student : public Person
//{
//	//里面没有定义print，名字，年龄，但是可以调用
//protected:
//	int _stuid;//学号
//};
//
//class Teacher : public Person
//{
//protected:
//	int _jobid;//学号
//};
//


//int main()
//{
//	Student s;
//	s.Print();
//	//子类没有定义，但是继承了父类就可以调用
//
//	Person p;
//	p.Print();
//
//	s._name = "张三";
//	s.Print();
//	//子类对象的改变不会影响父类
//	//是深拷贝
//	//函数还是同一个函数
//	return 0;
//}

//***************************************************************************************
//继承基类成员访问方式的变化：
//          使用继承方式： public继承           protected继承        private继承
// 父类的成员 ：
//    public：成员     派生的public             派生的protected     派生的private
//   
// protected：成员     派生的protected         派生的protected      派生的private
//  
//   private：成员      在派生类中不可见        在派生类中不可见     在派生类中不可见
// 
// public通过任何方式继承都是public
// protected只有在private继承时才变为private，其他时候都是保持protected
// private任何方式继承都是private
//  取低（更严格的）级别  public最高，private最低
//***************************************************************************************

//protected和private的区别：前者：子类继承后可以访问，后者：子类继承后还是不可访问


//不写继承方式class Student : Person     ：默认私有
//           struct Student : Person    ：默认公有
//（99%的情况） 一般来说都显示地公有继承：不变访问方式的继承



//子类对象，父类对象（指针，引用）能否互相赋值?（本来是不可以的，但是走特殊：）
//特殊：赋值兼容转换
//   派生类的对象可以赋值给基类的对象/指针/引用（寓意：把派生类中父类那一部分切来赋值过去
//   但是基类对象不能赋值给派生类对象，只能通过继承 
// （儿子可以切割自己给父亲，父亲不能给儿子）
// 儿子给父亲是因为可以切割，父亲给儿子，缺少成员
// 
// 
// 
//

/*

int main()
{
	Student s;
	Person p;

	//赋值兼容转换

	p = s;//不是类型转换/和类型转换赋值之间的截断和提升不同//没有产生临时对象
	//公有继承下：相重合的公有部分：切割给父类
	Person* ptr = &s;
	Person& ref = s;

	ptr->_name += 'x';//对指针进行改变
	ref._name += 'y';//对引用进行改变

	s._name += 'z';//对父类改变
	s.Print();
	//没有产生临时对象，都是用的同一块
	return 0;
}
*/


//继承中的作用域
//在继承体系中，基类和派生类都有独立的作用域（标明函数的域名）
//（同名，先找自己再找父）子类成员会屏蔽父类对同名成员的直接访问，这种情况叫隐藏，也叫重定义（在子类成员中，可以使用 基类::基类成员 显示访问）
// （同名的时候，想跳过子类中的同名成员，去访问父类的，那么就要表明（指定作用域）类域（父类下的...））
// bb.fun(0);
// bb.A::fun();
// 
// 需要注意的是，如果是成员函数的隐藏，只需要函数名相同就构成隐藏
//在实际的继承体系中，最好不要有同名成员


//默认生成的构造析构拷构
//派生类能否使用
/*
class Person
{
public:
	Person(const char* name="")
		:_name(name)
	{
		cout << "person()" << endl;
	}
	Person(const Person& p)
		:_name(p._name)
	{
		cout << "person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~perosn()" << endl;
	}
protected:
	string _name;
};


class Student : public Person
{
public:
	Student(const char* name="", int x=0, const char* address="")
		:Person(name)
		//规定父类的成员初始化，需要使用父类自己的方式不可以自己写
		//初始化方式：类名（参数）
		,_x(x)
		,_address(address)
	{}

	//深拷贝
	Student(const Student& st)
		:Person(st)
		//复用
		//如何可以调用父类的拷贝构造
		//用父类 + 参数
		//st这个子类赋值给person
		// （变成子类中切出来的父类那一部分的别名）公共部分
		////赋值兼容转换
		//
		,_x(st._x)
		,_address(st._address)
	{}

	//赋值拷贝(一般情况下不需要自己写（深拷贝new才需要写）)
	Student& operator=(const Student& st)
	{
		if (this != &st)
		{
			Person::operator=(st);//复用拷贝构造
			_x = st._x;
			_address = st._address;
		}
		return *this;
	}

	//由于多态的原因：析构函数的名字会被统一处理
	//被处理为destructor
	//都叫同一个名字（析构函数构成隐藏关系）
	//导致无法调用：
	//需要指定类域
	~Student()
	{
		//Person::~Person();//复用
		//这里（析构）不能显示调用
		//因为要保证子类析构再调用父类析构（这里会在子类析构之后自动调用r）。
		cout << "~student()" << endl;

		//delete [] _prt;
	}
	
protected:
	int _x = 1;
	string _address;
};
*/

//子类复用父类时：显示调用
//除了析构（析构不需要显示调用）
//统一处理为自动调用，再显示则就会多次析构

//析构的顺序和构造的顺序，是相反的类似栈（后初始化先析构）
//子类析构了，父亲才析构


//子类生成的构造：
//父类成员：整体  --  默认构造
// 子类自己的内置类型成员  --  一般不处理
// 子类自己的自定义类型成员  --默认构造

//子类生成的拷贝构造：  赋值重载和拷贝构造类似
//父类成员：整体  --  调用父类的拷贝构造
// 子类自己的内置类型成员  --  值拷贝
// 子类自己的自定义类型成员  --调用他的拷贝构造构造
// 一般不需要自己写
//!!!!!!!!!!什么情况下需要自己写拷贝构造（涉及深拷贝的问题）

//子类默认生成的析构函数
// 子类自己的内置类型成员  --  不处理
// 子类自己的自定义类型成员  --调析构
// 父类部分成员：调用析构
// 

//
//int main()
//{
//	Student s1("张三", 1, "成都市武侯区");
//	Student s2;//只继承父类，派生类不写构造，报错❌
//
//	Student s3 = s1;
//
//	//s3 = s2;
//	//❌栈溢出：子类赋值拷贝和父类的构成”隐藏“
//	// 导致自己调用自己
//	//要复用调用父类的赋值拷贝需要声明类域
//
//
//	return 0;
//}


//初始化保证先父后子
//初始化列表的顺序不是写出的顺序，而是声明的顺序
//析构保证先子后父
//显示调用（复用父类的析构：会造成父类先被析构）
//子类的父类部分已经被析构，再访问（析构）则会报错
//子类析构是可能会用到父类成员的

//结论：父类析构不是自己显式调用的，而是子类析构结束时，自动调用

//继承是一个类层次的复用


//友元关系不能继承
//父类的朋友不是儿子的朋友
//

//friend void Display(const Person& p, const Student& s);
//派生类的友元，父类能否使用？
//不能
 
//继承与静态成员
//继承体系内只能有一个static静态成员
//class Person
//{
//public:
//	Person() { ++_count; }
//protected:
//	string _name;
//public:
//	static int _count;
//};
//
//int Person::_count = 0;
////静态成员初始化在类外面
////既属于父类又属于子类
////属于整个继承体系
//
//class Student :public Person
//{
//protected:
//	int _stuNum;//学号
//};

//int main()
//{
//	Person p;
//	Student s;
//
//	cout << &Person::_count << endl;
//	cout << &Student::_count << endl;
//	//_count在整个体系中都是使用同一个_count，同一个地址，没有拷贝
//	//静态成员不存在在对象中，而是在静态区
//	return 0;
//}


//单继承/多继承
// 
// 在这个例子中，类D继承自B和C，而B和C都继承自A。
// 当创建D的对象时，会有两份A的数据成员，这可能导致数据冗余。
// 并且如果在D中访问a（假设a是A中的成员），会产生二义性，
// 因为编译器不知道是通过B路径还是C路径来访问A中的a。
// 
//只有一个/有多个直接父类
//class A ：public Student,public Teacher
//        :               ,

//多继承（西红柿：蔬菜，水果）导致：数据冗余和二义性
//访问有问题！！！！！！！！！

//继承两个类，导致name有两份，访问的时候指定类域可以解决二义性
//A aaa;
//aaa .Student::_name="小李";
//aaa .Teacher::_name="李老师";

//但是还是没有解决数据冗余


//虚拟继承

//当使用虚拟继承时，编译器会确保在D的对象中只有一份A的数据成员。
// 具体来说，编译器会为虚拟继承创建一个虚基类表（vbtable），类似于虚函数表（vtable）。
// 这个表用于记录每个虚基类的偏移量，这样在访问虚基类的成员时，可以正确地定位到唯一的那份数据。
// 
// 可以解决菱形继承的二义性和数据冗余的问题
//让子类将继承父类的时候使用虚拟继承
//: 后面接virtual再接继承方式public...
//virtual也解决了二义性的问题
//aaa._name="李兄";

//虚继承应该在继承的开始位置(B,C)，不一定是完全的菱形
//         A
// 
//B(virtual)                 C(virtual)
//    
// 
//   D
// 
//           E




// 继承（是什么）和组合（有什么）
// 继承：白箱复用（内部细节可见性，派生类和基类的依赖关系很强，耦合性高）
// 
// 对象组合：黑箱复用（内部细节不可见性，依赖关系不强，代码维护性好，不容易发生连锁反应）
// 如果要实现多态，还是要使用继承
// 类之间的关系可以使用组合就用组合
// 白盒测试的技术难度更高
//































